﻿/*
 *  Author: Louis A. Leskoviansky
 *  Company: Artemis Solutions Group (http://www.artemis-solutions.com)
 */


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace AjaxControlFramework
{
    public abstract class StateStrategy : PageStatePersister, IAjaxControlStrategy
    {
        //------// Properties \\--------------------------------------------\\
        public virtual IAjaxControl Control { get; set; }

        private Control _targetControl = null;
        public virtual Control TargetControl
        {
            get
            {
                if (_targetControl == null)
                {
                    _targetControl = Control as Control;
                }

                return _targetControl;
            }
            set
            {
                _targetControl = value;
            }
        }


        private Page _parentPage = null;
        public virtual Page ParentPage
        {
            get
            {
                if (_parentPage == null)
                {
                    if (Control == null) { return null; }

                    if ((Control as Control).Page == null)
                    {
                        _parentPage = (HttpContext.Current.CurrentHandler as Page);
                    }
                    else
                    {
                        _parentPage = (Control as Control).Page;
                    }
                }

                return _parentPage;
            }
        }


        protected virtual List<IAjaxControl> ChildAjaxControls { get; set; }

        public virtual bool StateLoaded { get; set; }
        public virtual bool StateSaved { get; set; }


        private static Type TypeOfControl = typeof(System.Web.UI.Control);



        public delegate object SaveStateMethod(Control targetControl);

        private static object SaveViewStateRecursiveLock = new object();
        private static SaveStateMethod _saveViewStateRecursive = null;
        public static SaveStateMethod SaveViewStateRecursive
        {
            get
            {
                if (_saveViewStateRecursive == null)
                {
                    lock (SaveViewStateRecursiveLock)
                    {
                        if (_saveViewStateRecursive == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "SaveViewStateRecursive".
                            MethodInfo saveViewStateRecursiveMethodInfo = TypeOfControl.GetMethod("SaveViewStateRecursive", (BindingFlags.NonPublic | BindingFlags.Instance), null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("SaveViewStateRecursive", typeof(object), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, saveViewStateRecursiveMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _saveViewStateRecursive = (SaveStateMethod)dynamicMethod.CreateDelegate(typeof(SaveStateMethod));
                        }
                    }
                }

                return _saveViewStateRecursive;
            }
        }


        private static object SaveControlStateInternalLock = new object();
        private static SaveStateMethod _saveControlStateInternal = null;
        public static SaveStateMethod SaveControlStateInternal
        {
            get
            {
                if (_saveControlStateInternal == null)
                {
                    lock (SaveControlStateInternalLock)
                    {
                        if (_saveControlStateInternal == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "SaveControlStateInternal".
                            MethodInfo saveControlStateInternalMethodInfo = TypeOfControl.GetMethod("SaveControlStateInternal", (BindingFlags.NonPublic | BindingFlags.Instance), null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("SaveControlStateInternal", typeof(object), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, saveControlStateInternalMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _saveControlStateInternal = (SaveStateMethod)dynamicMethod.CreateDelegate(typeof(SaveStateMethod));
                        }
                    }
                }

                return _saveControlStateInternal;
            }
        }


        public delegate void LoadStateMethod(Control targetControl, object state);

        private static object LoadViewStateRecursiveLock = new object();
        private static LoadStateMethod _loadViewStateRecursive = null;
        public static LoadStateMethod LoadViewStateRecursive
        {
            get
            {
                if (_loadViewStateRecursive == null)
                {
                    lock (LoadViewStateRecursiveLock)
                    {
                        if (_loadViewStateRecursive == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "LoadViewStateRecursive".
                            MethodInfo loadViewStateRecursiveMethodInfo = TypeOfControl.GetMethod("LoadViewStateRecursive", (BindingFlags.NonPublic | BindingFlags.Instance), null, new Type[] { typeof(object) }, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("LoadViewStateRecursive", typeof(void), new Type[] { TypeOfControl, typeof(object) }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldarg_1);
                            ilGenerator.Emit(OpCodes.Call, loadViewStateRecursiveMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _loadViewStateRecursive = (LoadStateMethod)dynamicMethod.CreateDelegate(typeof(LoadStateMethod));
                        }
                    }
                }

                return _loadViewStateRecursive;
            }
        }


        private static object LoadControlStateInternalLock = new object();
        private static LoadStateMethod _loadControlStateInternal = null;
        public static LoadStateMethod LoadControlStateInternal
        {
            get
            {
                if (_loadControlStateInternal == null)
                {
                    lock (LoadControlStateInternalLock)
                    {
                        if (_loadControlStateInternal == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "LoadControlStateInternal".
                            MethodInfo loadControlStateInternalMethodInfo = TypeOfControl.GetMethod("LoadControlStateInternal", (BindingFlags.NonPublic | BindingFlags.Instance), null, new Type[] { typeof(object) }, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("LoadControlStateInternal", typeof(void), new Type[] { TypeOfControl, typeof(object) }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldarg_1);
                            ilGenerator.Emit(OpCodes.Call, loadControlStateInternalMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _loadControlStateInternal = (LoadStateMethod)dynamicMethod.CreateDelegate(typeof(LoadStateMethod));
                        }
                    }
                }

                return _loadControlStateInternal;
            }
        }
        //------\\ Properties //--------------------------------------------//



        //------// Constructors \\------------------------------------------\\
        protected StateStrategy(Control control)
            : base(control is Page ? (Page)control : (Page)HttpContext.Current.CurrentHandler)
        {
            if (control == null) { throw new ArgumentNullException("control"); }
            if (!(control is IAjaxControl)) { throw new ArgumentException("A reference of type IAjaxControl was expected.", "control"); }

            Control = control as IAjaxControl;
        }
        //------\\ Constructors //------------------------------------------//



        //------// Methods \\-----------------------------------------------\\
        public virtual void Save(bool saveStateOfChildren)
        {
            if (saveStateOfChildren)
            {
                if (ChildAjaxControls != null && ChildAjaxControls.Count > 0)
                {
                    foreach (IAjaxControl ajaxControl in ChildAjaxControls)
                    {
                        if (ajaxControl.StateStrategy != null && ajaxControl.StateStrategy.StateLoaded)
                        {
                            ajaxControl.StateStrategy.Save(false);
                        }
                    }
                }
            }


            ControlState = GenerateControlStateTable();
            ViewState = SaveViewStateRecursive(TargetControl);

            PersistState();

            StateSaved = true;
        }


        protected virtual Hashtable GenerateControlStateTable()
        {
            if (ParentPage == null) { return null; }

            List<Control> controlsRequiringControlState = AjaxControlUtils.FindControlsThatRequireControlState(ParentPage, TargetControl);

            if (controlsRequiringControlState.Count > 0)
            {
                Hashtable controlStateTable = new Hashtable(controlsRequiringControlState.Count);

                foreach (Control control in controlsRequiringControlState)
                {
                    controlStateTable.Add(control.UniqueID, SaveControlStateInternal(control));
                }

                return controlStateTable;
            }

            return null;
        }


        // This is a base implementation and is intended to be overriden.
        public override void Save()
        {
            Save(true);
        }


        public virtual void Load(bool loadStateOfChildren)
        {
            if (loadStateOfChildren)
            {
                ChildAjaxControls = AjaxControlUtils.FindChildControlsByType<IAjaxControl>(TargetControl);

                if (ChildAjaxControls != null && ChildAjaxControls.Count > 0)
                {
                    foreach (IAjaxControl ajaxControl in ChildAjaxControls)
                    {
                        if (ajaxControl.StateStrategy != null)
                        {
                            Control childControl = (ajaxControl as Control);

                            if (childControl != null && childControl.EnableViewState)
                            {
                                ajaxControl.StateStrategy.Load(false);
                                childControl.EnableViewState = false;
                            }
                        }
                    }
                }
            }


            RestoreState();


            // Retrieve the ViewState and ControlState objects returned by the SaveState delegates.
            if (ViewState != null || ControlState != null)
            {
                LoadControlStateTable(ControlState as Hashtable); // Load ControlState first.
                LoadViewStateRecursive(TargetControl, ViewState);

                StateLoaded = true;
            }
            else
            {
                StateLoaded = false;
            }
        }


        protected virtual void LoadControlStateTable(Hashtable controlStateTable)
        {
            if (controlStateTable == null || controlStateTable.Count == 0) { return; }

            List<Control> controlsRequiringControlState = AjaxControlUtils.FindControlsThatRequireControlState(ParentPage, TargetControl);

            foreach (Control control in controlsRequiringControlState)
            {
                object controlState = controlStateTable[control.UniqueID];

                if (controlState != null)
                {
                    LoadControlStateInternal(control, controlState);
                }
            }
        }


        // This is a base implementation and is intended to be overriden.
        public override void Load()
        {
            Load(true);
        }


        protected abstract void PersistState();
        protected abstract void RestoreState();
        //------\\ Methods //-----------------------------------------------//
    }
}
